﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace XingLucifer.Devices
{
    public class KVPLC : PLCBase
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        public int ID { get; set; }
        public string IP { get; set; }
        public int PORT { get; set; }
        public int Timeout { get; set; }
        bool IsRunning { get; set; }
        bool IsConnected { get; set; }
        object locker = new object();
        public KVPLC(string ip, int timeout = 3000)
        {
            var vals = ip.Split(':');
            IP = vals[0].Trim();
            PORT = int.Parse(vals[1]);
            Timeout = timeout;
            //ID = id;
        }
        public bool Connection()
        {
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(IP, PORT);
                socket.ReceiveTimeout = Timeout;
                socket.SendTimeout = Timeout;
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public void Close()
        {
            if (socket != null)
            {
                try
                {
                    socket.Close();
                    socket.Dispose();
                }
                catch (Exception)
                {

                }
                socket = null;
            }
            IsConnected = false;
        }
        /// <summary>
        ///   方法来将字符串转换为一个 List<UInt16>，其中每两个字节被组合成一个 UInt16 字，每两个字节存储在一个字内，一个字字节存储在低八位，另一个字节存储在高八位
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public bool WriteStringAsUInt16(string addr, string values)
        {
            var vals = new List<UInt16>();

            for (int i = 0; i < values.Length; i += 2)
            {
                char lowChar = values[i];
                char highChar = (i + 1 < values.Length) ? values[i + 1] : '\0';  // '\0' 表示空字符

                byte lowByte = (byte)(lowChar & 0xFF);      // 取低八位
                byte highByte = (byte)(highChar & 0xFF);    // 取高八位

                UInt16 combinedValue = (UInt16)((lowByte << 8) | highByte);  // 组合成一个 UInt16 值，互换高低字节
                vals.Add(combinedValue);
            }

            return Write<UInt16>(addr, vals.ToArray());
        }

        public bool WriteString(string addr, string values)
        {
            var z1 = Encoding.Default.GetBytes(values);
            var vals = new List<UInt16>();
            for (var i = 0; i < z1.Length; i++)
            {
                var b = z1[i];
                var bytes = new List<byte>();
                if (b > 127)
                {
                    bytes.Add(z1[i]);
                    bytes.Add(z1[i + 1]);
                    i++;
                }
                else
                {
                    bytes.Add(z1[i]);
                    bytes.Add((byte)0);
                }
                var hex = "";
                foreach (var bb in bytes)
                {
                    hex += bb.ToString("X2");
                }
                var bbb = UInt16.Parse(hex, System.Globalization.NumberStyles.HexNumber);
                vals.Add(bbb);
            }
            return Write<UInt16>(addr, vals.ToArray());
        }
        public bool Write<T>(string addr, T[] values)
        {
            try
            {
                if (!IsConnected)
                    IsConnected = Connection();
                if (!IsConnected)
                    return false;

                var wcmd = $"WRS {addr} {values.Length} {string.Join(" ", values)}\r\n";
                var bytes = new byte[4096];
                var readLen = 0;
                lock (locker)
                {
                    socket.Send(Encoding.Default.GetBytes(wcmd));
                    readLen = socket.Receive(bytes);
                }
                var str = Encoding.Default.GetString(bytes.Take(readLen).ToArray());
                return str.Contains("OK") ? true : false;
            }
            catch (Exception ex)
            {
                Close();
                return false;
            }
        }

        public static string ConvertToBinaryString(ushort number)
        {
            string binaryString = "";
            for (int i = 15; i >= 0; i--)
            {
                char binaryDigit = (number & (1 << i)) == 0 ? '0' : '1';
                binaryString += binaryDigit;
            }
            return binaryString;
        }
        public bool ReadAndReturnBit(string addr)
        {
            try
            {
                // 构造读取指令，读取地址去掉后两位先保留
                string addrWithoutLastTwoDigits = addr.Substring(0, addr.Length - 2);
                var binaryResult = Read(addrWithoutLastTwoDigits, 1);
                if (binaryResult == null) return false;
                // 获取该数字的二进制表示
                string binaryString = ConvertToBinaryString(binaryResult[0]);

                // 从地址字符串中提取后两位数字
                string lastTwoDigits = addr.Substring(addr.Length - 2);

                // 将后两位数字转换为整数值
                int reservedValue = int.Parse(lastTwoDigits, NumberStyles.HexNumber);

                //倒数
                int index = binaryString.Length - reservedValue - 1;

                return binaryString[index].ToString() == "1" ? true : false;
            }
            catch (Exception ex)
            {
                // 出现异常时，关闭连接并设置连接状态为false
                return false;
            }
        }

        public List<UInt32> ReadUInt32(string addr, int count)

        {
            try
            {
                if (!IsConnected)
                    IsConnected = Connection();
                if (!IsConnected)
                    return null;

                var bytes = new byte[1024];
                var cmd = $"RDS {addr} {count * 2}\r\n"; // 读取两倍的数量，因为每个UInt32由两个UInt16表示
                var str1 = "";
                var vals = new List<UInt32>();
                lock (locker)
                {
                    socket.Send(Encoding.Default.GetBytes(cmd));
                    var len = socket.Receive(bytes);
                    bytes = bytes.Take(len).ToArray();
                }

                str1 = Encoding.Default.GetString(bytes).Trim();
                var str1s = str1.Split(' ');
                for (var i = 0; i < str1s.Length; i += 2)
                {
                    var low = UInt16.Parse(str1s[i]);
                    var high = UInt16.Parse(str1s[i + 1]);
                    var val = ((UInt32)high << 16) | low; // 将高16位和低16位合并为一个UInt32
                    vals.Add(val);
                }
                return vals;
            }
            catch (Exception ex)
            {
                Close();
                return null;
            }
        }
        public List<UInt16> Read(string addr, int count)
        {
            try
            {
                if (!IsConnected)
                    IsConnected = Connection();
                if (!IsConnected)
                    return null;

                var bytes = new byte[1024];
                var cmd = $"RDS {addr} {count}\r\n";
                var str1 = "";
                var vals = new List<UInt16>();
                lock (locker)
                {
                    socket.Send(Encoding.Default.GetBytes(cmd));
                    var len = socket.Receive(bytes);
                    bytes = bytes.Take(len).ToArray();
                }

                str1 = Encoding.Default.GetString(bytes).Trim();
                // $"PLC:{string.Join(";",str1)}".Info();
                var str1s = str1.Split(' ');
                for (var i = 0; i < str1s.Length; i++)
                {
                    vals.Add(UInt16.Parse(str1s[i]));
                }
                return vals;
            }
            catch (Exception ex)
            {
                //socket.Close();
                //IsConnected = false;
                Close();
                return null;
            }
        }

        private SemaphoreSlim semaphore = new SemaphoreSlim(1);

        public async Task<List<UInt16>> ReadAsync(string addr, int count)
        {
            try
            {
                if (!IsConnected)
                    IsConnected = await ConnectionAsync();
                if (!IsConnected)
                    return null;

                var bytes = new byte[1024];
                var cmd = $"RDS {addr} {count}\r\n";
                var str1 = "";
                var vals = new List<UInt16>();

                await semaphore.WaitAsync(); // 等待获取异步锁
                try
                {
                    await socket.SendAsync(new ArraySegment<byte>(Encoding.Default.GetBytes(cmd)), SocketFlags.None);
                    var len = await socket.ReceiveAsync(new ArraySegment<byte>(bytes), SocketFlags.None);
                    bytes = bytes.Take(len).ToArray();
                }
                finally
                {
                    semaphore.Release(); // 释放异步锁
                }

                str1 = Encoding.Default.GetString(bytes).Trim();
                var str1s = str1.Split(' ');
                for (var i = 0; i < str1s.Length; i++)
                {
                    vals.Add(UInt16.Parse(str1s[i]));
                }
                return vals;
            }
            catch (Exception ex)
            {
                //socket.Close();
                //IsConnected = false;
                Close();
                return null;
            }
        }
        public async Task<bool> ConnectionAsync()
        {
            try
            {
                await Task.Run(() =>
                {
                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    socket.Connect(IP, PORT);
                    socket.ReceiveTimeout = Timeout;
                    socket.SendTimeout = Timeout;
                });
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        public string ReadString(string addr, int count)
        {
            try
            {
                var values = Read(addr, count);
                var bytes = new List<byte>();
                if (values != null)
                    for (int i = 0; i < values.Count; i++)
                    {
                        var v = values[i];
                        var h = (byte)((v >> 8) & 0x00ff);
                        var l = (byte)(v & 0x00ff);
                        if (h > 0)
                            bytes.Add(h);
                        if (l > 0)
                            bytes.Add(l);
                    }
                var s = Encoding.Default.GetString(bytes.ToArray());
                return Encoding.Default.GetString(bytes.ToArray());
            }

            catch (Exception ex)
            {
                //socket.Close();
                //IsConnected = false;
                Close();
                return "";
            }
        }

        public string ToString(IEnumerable<UInt16> values)
        {
            try
            {
                var bytes = new List<byte>();
                values.ToList().ForEach(v =>
                {
                    if (v > 0)
                    {
                        var h = (byte)((v >> 8) & 0x00ff);
                        var l = (byte)(v & 0x00ff);
                        if (h > 0)
                            bytes.Add(h);
                        if (l > 0)
                            bytes.Add(l);
                    }
                });
                var str = Encoding.Default.GetString(bytes.ToArray()).Trim();
                return str;
            }
            catch (Exception ex)
            {
                return "";
            }
        }

        public bool ReadBool(string addr)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 清空地址，用于清空批次号、型号等
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public bool ClearPLCAddress(string addr, int length)
        {
            var vals = new UInt16[length];
            for (int i = 0; i < length; i++)
            {
                vals[i] = 0;
            }

            return Write<UInt16>(addr, vals);
        }
    }
}
